<?php

namespace App\Services;

use App\Constant;
use App\Models\UserWallet;
use Carbon\Carbon;
use DateTime;

class SdPayService
{
    private $host;
    private $payNotifyUrl;
    private $walletNotifyUrl;
    private $merchNo;
    private $privateKey;

    public function __construct($configs)
    {
        $this->host = config('services.sdpay.host');
        $this->payNotifyUrl = config('services.sdpay.pay_notify_url');
        $this->walletNotifyUrl = config('services.sdpay.wallet_notify_url');
        $this->merchNo = \Arr::get($configs, 'sdpay_merch_no');
        $this->privateKey = \Arr::get($configs, 'sdpay_rsa_private_key');
    }

    public function getWalletUrl(UserWallet $wallet, $redirectUrl)
    {
        return $this->host . '/pay/h5/cloud?' . $this->getUnifiedOrderQueryString('00000001', [
            'pay_extra' => ['userId' => $wallet->wallet_user_no, 'nickName' => '昵称未设置'],
            'notify_url' => $this->walletNotifyUrl,
            'return_url' => $redirectUrl,
        ]);
    }

    public function getUnionPayLink($orderNo, $amount, $goodsName, $userNo, $walletUserNo, $redirectUrl, DateTime $expireTime, $extra = [])
    {
        if (\Arr::has($extra, ['payer', 'payeeList'])) {
            $payExtra = [
                'orderDesc' => \Arr::get($extra, 'orderDesc'),
                'accountingMode' => \Arr::get($extra, 'accountingMode'),
                'payeeList' => \Arr::get($extra, 'payeeList'),
                'payer' => \Arr::get($extra, 'payer'),
            ];

            return $this->host . '/pay/h5/cloud?' . $this->getUnifiedOrderQueryString('04010004', [ //C2C担保消费
                'order_no' => $orderNo,
                'order_amt' => $amount,
                'pay_extra' => $payExtra,
                'return_url' => $redirectUrl,
                'goods_name' => $goodsName,
                'expire_time' => Carbon::parse($expireTime),
            ]);
        }
        if (\Arr::has($extra, 'resale_user_wallet_no')) {
            $payExtra = [
                'operationType' => '1', //转账申请
                'recvUserId' => \Arr::get($extra, 'resale_user_wallet_no'), //收款方会员编号
                'bizType' => '2', //1：转账确认模式 2：实时转账模式
                'payUserId' => $walletUserNo,   //付款方会员编号
                'remark' => $goodsName,
            ];
            $serviceFee = \Arr::get($extra, 'resale_service_fee', 0);
            if ($serviceFee > 0) {
                $payExtra['userFeeAmt'] = $serviceFee;
            }

            return $this->host . '/pay/h5/cloud?' . $this->getUnifiedOrderQueryString('04010003', [  //云账户C2C转账
                'order_no' => $orderNo,
                'order_amt' => $amount,
                'pay_extra' => $payExtra,
                'return_url' => $redirectUrl,
                'goods_name' => $goodsName,
                'expire_time' => Carbon::parse($expireTime),
            ]);
        }

        if ($walletUserNo) {
            return $this->host . '/pay/h5/cloud?' . $this->getUnifiedOrderQueryString('04010001', [  //云账户C2B
                'order_no' => $orderNo,
                'order_amt' => $amount,
                'pay_extra' => ['userId' => $walletUserNo, 'nickName' => '昵称未设置', 'accountType' => '1'],
                'return_url' => $redirectUrl,
                'goods_name' => $goodsName,
                'expire_time' => Carbon::parse($expireTime),
            ]);
        }

        return $this->host . '/pay/h5/fastpayment?' . $this->getUnifiedOrderQueryString('05030001', [  //一键快捷
            'order_no' => $orderNo,
            'order_amt' => $amount,
            'pay_extra' => ['userId' => $userNo],
            'return_url' => $redirectUrl,
            'goods_name' => $goodsName,
            'expire_time' => Carbon::parse($expireTime),
        ]);
    }

    public function queryOrder($orderNo)
    {
        $url = 'https://cashier.sandpay.com.cn/gateway/api/order/query';
        $method = 'sandpay.trade.query';
        $productId = '00000016';
        if (\Str::startsWith($orderNo, 'WALLET_')) {
            $productId = '00002046';
        }
        $data = [
            'head' => [
                'version' => '1.0',
                'method' => $method,
                'productId' => $productId,
                'accessType' => 1,
                'mid' => $this->merchNo,
                'channelType' => '08',
                'reqTime' => date('YmdHis', time()),
            ],
            'body' => [
                'orderCode' => $orderNo,
            ],
        ];
        $postData = [
            'charset' => 'utf-8',
            'signType' => '01',
            'data' => json_encode($data),
            'sign' => $this->sign(json_encode($data)),
        ];
        \Log::channel(Constant::LOG_CHANNEL_SDPAY)->info('ORDER_QUERY:' . $orderNo, $data);
        $resp = \Http::asForm()->post($url, $postData);
        $result = $this->parseResult($resp->body());
        $data = \Arr::get($result, 'data', []);
        \Log::channel(Constant::LOG_CHANNEL_SDPAY)->info('ORDER_QUERY_RESP_' . $orderNo, $data);

        $respCode = \Arr::get($data, 'head.respCode');
        if ('030006' == $respCode) {
            return ['order_no' => $orderNo, 'status' => 'PROCESSING'];
        }
        if ('000000' == $respCode) {
            return ['order_no' => $orderNo, 'status' => 'SUCCESS'];
        }

        return ['order_no' => $orderNo, 'status' => 'UNKNOWN'];
    }

    /** 杉德支付未提供关闭订单接口 */
    public function closeOrder($orderId, $uniqueOrderNo)
    {
        return '';
    }

    // curl.解析返回数据
    protected function parseResult($result)
    {
        $arr = [];
        $response = urldecode($result);
        $arrStr = explode('&', $response);
        foreach ($arrStr as $str) {
            $p = strpos($str, '=');
            $key = substr($str, 0, $p);
            $value = substr($str, $p + 1);
            if ('data' === $key) {
                $value = json_decode($value, true);
            }
            $arr[$key] = $value;
        }

        return $arr;
    }

    private function getUnifiedOrderQueryString($productCode, $data)
    {
        \Log::channel(Constant::LOG_CHANNEL_SDPAY)->info('UNIFIED_ORDER_' . $productCode, $data);
        $expireTime = \Arr::get($data, 'expire_time', now()->addMinutes(30));
        $params = [
            'version' => 10,
            'mer_no' => $this->merchNo, //商户号
            'mer_order_no' => \Arr::get($data, 'order_no', date('YmdHis')), //商户唯一订单号
            'create_time' => date('YmdHis'),
            'expire_time' => $expireTime->format('YmdHis'),
            'order_amt' => \Arr::get($data, 'order_amt', 0.11), //订单支付金额
            'notify_url' => \Arr::get($data, 'notify_url', $this->payNotifyUrl),
            'return_url' => \Arr::get($data, 'return_url', ''), //订单前端页面跳转地址
            'create_ip' => '172_12_12_2',
            'goods_name' => \Arr::get($data, 'goods_name', '无'),
            'store_id' => '000000',
            'product_code' => $productCode,
            'clear_cycle' => '3',
            //pay_extra参考语雀文档4.3
            'pay_extra' => json_encode(\Arr::get($data, 'pay_extra')),
            'accsplit_flag' => 'NO',
            'jump_scheme' => '',
            'meta_option' => json_encode([['s' => 'Android', 'n' => 'wxDemo', 'id' => 'com.pay.paytypetest', 'sc' => 'com.pay.paytypetest']]),
            'sign_type' => 'RSA',
        ];
        $dataForSign = $params;
        unset($dataForSign['goods_name'], $dataForSign['jump_scheme'], $dataForSign['expire_time'], $dataForSign['product_code'], $dataForSign['clear_cycle'], $dataForSign['meta_option']);

        $sign = $this->sign($this->getSignContent($dataForSign));
        $params['sign'] = $sign;

        return http_build_query($params);
    }

    private function sign($str)
    {
        $pem = "-----BEGIN PRIVATE KEY-----\n" .
            wordwrap($this->privateKey, 64, "\n", true) .
            "\n-----END PRIVATE KEY-----";
        openssl_sign($str, $sign, $pem);

        return base64_encode($sign);
    }

    private function getSignContent($params)
    {
        ksort($params);

        $stringToBeSigned = '';
        $i = 0;
        foreach ($params as $k => $v) {
            if (false === $this->checkEmpty($v) && '@' != substr($v, 0, 1)) {
                if (0 == $i) {
                    $stringToBeSigned .= "{$k}" . '=' . "{$v}";
                } else {
                    $stringToBeSigned .= '&' . "{$k}" . '=' . "{$v}";
                }
                ++$i;
            }
        }

        unset($k, $v);

        return $stringToBeSigned;
    }

    private function checkEmpty($value)
    {
        if (!isset($value)) {
            return true;
        }
        if (null === $value) {
            return true;
        }
        if ('' === trim($value)) {
            return true;
        }

        return false;
    }
}
